#pragma once
#include <linux/ioctl.h>
#include <cstdint>
#include <cstddef>

namespace ws2812b
{

    //
    // Raspberry Pi Hardware Query 树莓派硬件查询功能
    //
    struct rpi_hw_t
    {
#define RPI_HWVER_TYPE_UNKNOWN 0
#define RPI_HWVER_TYPE_PI1 1
#define RPI_HWVER_TYPE_PI2 2
#define RPI_HWVER_TYPE_PI4 3
        uint32_t hwver;
        uint32_t type;
        uint32_t periph_base;
        uint32_t videocore_base;
        const char *desc;
    };

    const rpi_hw_t *rpi_hw_detect(void);

    //
    // Raspberry Pi GPIO Func 树莓派GPIO控制功能
    //

    struct gpio_t
    {
        uint32_t fsel[6]; // GPIO Function Select
        uint32_t resvd_0x18;
        uint32_t set[2]; // GPIO Pin Output Set
        uint32_t resvd_0x24;
        uint32_t clr[2]; // GPIO Pin Output Clear
        uint32_t resvd_0x30;
        uint32_t lev[2]; // GPIO Pin Level
        uint32_t resvd_0x3c;
        uint32_t eds[2]; // GPIO Pin Event Detect Status
        uint32_t resvd_0x48;
        uint32_t ren[2]; // GPIO Pin Rising Edge Detect Enable
        uint32_t resvd_0x54;
        uint32_t fen[2]; // GPIO Pin Falling Edge Detect Enable
        uint32_t resvd_0x60;
        uint32_t hen[2]; // GPIO Pin High Detect Enable
        uint32_t resvd_0x6c;
        uint32_t len[2]; // GPIO Pin Low Detect Enable
        uint32_t resvd_0x78;
        uint32_t aren[2]; // GPIO Pin Async Rising Edge Detect
        uint32_t resvd_0x84;
        uint32_t afen[2]; // GPIO Pin Async Falling Edge Detect
        uint32_t resvd_0x90;
        uint32_t pud;       // GPIO Pin Pull up/down Enable
        uint32_t pudclk[2]; // GPIO Pin Pull up/down Enable Clock
        uint32_t resvd_0xa0[4];
        uint32_t test;
    } __attribute__((packed, aligned(4)));

#define GPIO_OFFSET (0x00200000)

    static inline void gpio_function_set(volatile gpio_t *gpio, uint8_t pin, uint8_t function);
    static inline void gpio_level_set(volatile gpio_t *gpio, uint8_t pin, uint8_t level);
    static inline void gpio_output_set(volatile gpio_t *gpio, uint8_t pin, uint8_t output);

    //
    // Raspberry Pi CLK Func 树莓派CLK控制功能
    //

    struct cm_clk_t
    {
        uint32_t ctl;
#define CM_CLK_CTL_PASSWD (0x5a << 24)
#define CM_CLK_CTL_MASH(val) ((val & 0x3) << 9)
#define CM_CLK_CTL_FLIP (1 << 8)
#define CM_CLK_CTL_BUSY (1 << 7)
#define CM_CLK_CTL_KILL (1 << 5)
#define CM_CLK_CTL_ENAB (1 << 4)
#define CM_CLK_CTL_SRC_GND (0 << 0)
#define CM_CLK_CTL_SRC_OSC (1 << 0)
#define CM_CLK_CTL_SRC_TSTDBG0 (2 << 0)
#define CM_CLK_CTL_SRC_TSTDBG1 (3 << 0)
#define CM_CLK_CTL_SRC_PLLA (4 << 0)
#define CM_CLK_CTL_SRC_PLLC (5 << 0)
#define CM_CLK_CTL_SRC_PLLD (6 << 0)
#define CM_CLK_CTL_SRC_HDMIAUX (7 << 0)
        uint32_t div;
#define CM_CLK_DIV_PASSWD (0x5a << 24)
#define CM_CLK_DIV_DIVI(val) ((val & 0xfff) << 12)
#define CM_CLK_DIV_DIVF(val) ((val & 0xfff) << 0)
    } __attribute__((packed, aligned(4)));

/*
 * PWM and PCM clock offsets from https://www.scribd.com/doc/127599939/BCM2835-Audio-clocks
 *
 */
#define CM_PCM_OFFSET (0x00101098)
#define CM_PWM_OFFSET (0x001010a0)

//
// memory management function 内存管理功能
//
#define MAJOR_NUM 100
#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *)

#define DEV_MEM "/dev/mem"
#define DEV_GPIOMEM "/dev/gpiomem"
    int mbox_open(void);
    void mbox_close(int file_desc);

    unsigned get_version(int file_desc);
    unsigned mem_alloc(int file_desc, unsigned size, unsigned align, unsigned flags);
    unsigned mem_free(int file_desc, unsigned handle);
    unsigned mem_lock(int file_desc, unsigned handle);
    unsigned mem_unlock(int file_desc, unsigned handle);
    void *mapmem(unsigned base, unsigned size, const char *mem_dev);
    void *unmapmem(void *addr, unsigned size);

    unsigned execute_code(int file_desc, unsigned code, unsigned r0, unsigned r1, unsigned r2, unsigned r3, unsigned r4, unsigned r5);
    unsigned execute_qpu(int file_desc, unsigned num_qpus, unsigned control, unsigned noflush, unsigned timeout);
    unsigned qpu_enable(int file_desc, unsigned enable);

    /*
     * DMA Control Block in Main Memory
     *  主存储器中的DMA控制块
     * Note: Must start at a 256 byte aligned address.
     *       Use corresponding register field definitions.
     */
    struct dma_cb_t
    {
        uint32_t ti;
        uint32_t source_ad;
        uint32_t dest_ad;
        uint32_t txfr_len;
        uint32_t stride;
        uint32_t nextconbk;
        uint32_t resvd_0x18[2];
    } __attribute__((packed, aligned(4)));

    /*
     * DMA register set
     */
    struct dma_t
    {
        uint32_t cs;
#define RPI_DMA_CS_RESET (1 << 31)
#define RPI_DMA_CS_ABORT (1 << 30)
#define RPI_DMA_CS_DISDEBUG (1 << 29)
#define RPI_DMA_CS_WAIT_OUTSTANDING_WRITES (1 << 28)
#define RPI_DMA_CS_PANIC_PRIORITY(val) ((val & 0xf) << 20)
#define RPI_DMA_CS_PRIORITY(val) ((val & 0xf) << 16)
#define RPI_DMA_CS_ERROR (1 << 8)
#define RPI_DMA_CS_WAITING_OUTSTANDING_WRITES (1 << 6)
#define RPI_DMA_CS_DREQ_STOPS_DMA (1 << 5)
#define RPI_DMA_CS_PAUSED (1 << 4)
#define RPI_DMA_CS_DREQ (1 << 3)
#define RPI_DMA_CS_INT (1 << 2)
#define RPI_DMA_CS_END (1 << 1)
#define RPI_DMA_CS_ACTIVE (1 << 0)
        uint32_t conblk_ad;
        uint32_t ti;
#define RPI_DMA_TI_NO_WIDE_BURSTS (1 << 26)
#define RPI_DMA_TI_WAITS(val) ((val & 0x1f) << 21)
#define RPI_DMA_TI_PERMAP(val) ((val & 0x1f) << 16)
#define RPI_DMA_TI_BURST_LENGTH(val) ((val & 0xf) << 12)
#define RPI_DMA_TI_SRC_IGNORE (1 << 11)
#define RPI_DMA_TI_SRC_DREQ (1 << 10)
#define RPI_DMA_TI_SRC_WIDTH (1 << 9)
#define RPI_DMA_TI_SRC_INC (1 << 8)
#define RPI_DMA_TI_DEST_IGNORE (1 << 7)
#define RPI_DMA_TI_DEST_DREQ (1 << 6)
#define RPI_DMA_TI_DEST_WIDTH (1 << 5)
#define RPI_DMA_TI_DEST_INC (1 << 4)
#define RPI_DMA_TI_WAIT_RESP (1 << 3)
#define RPI_DMA_TI_TDMODE (1 << 1)
#define RPI_DMA_TI_INTEN (1 << 0)
        uint32_t source_ad;
        uint32_t dest_ad;
        uint32_t txfr_len;
#define RPI_DMA_TXFR_LEN_YLENGTH(val) ((val & 0xffff) << 16)
#define RPI_DMA_TXFR_LEN_XLENGTH(val) ((val & 0xffff) << 0)
        uint32_t stride;
#define RPI_DMA_STRIDE_D_STRIDE(val) ((val & 0xffff) << 16)
#define RPI_DMA_STRIDE_S_STRIDE(val) ((val & 0xffff) << 0)
        uint32_t nextconbk;
        uint32_t debug;
    } __attribute__((packed, aligned(4)));

#define DMA0_OFFSET (0x00007000)
#define DMA1_OFFSET (0x00007100)
#define DMA2_OFFSET (0x00007200)
#define DMA3_OFFSET (0x00007300)
#define DMA4_OFFSET (0x00007400)
#define DMA5_OFFSET (0x00007500)
#define DMA6_OFFSET (0x00007600)
#define DMA7_OFFSET (0x00007700)
#define DMA8_OFFSET (0x00007800)
#define DMA9_OFFSET (0x00007900)
#define DMA10_OFFSET (0x00007a00)
#define DMA11_OFFSET (0x00007b00)
#define DMA12_OFFSET (0x00007c00)
#define DMA13_OFFSET (0x00007d00)
#define DMA14_OFFSET (0x00007e00)
#define DMA15_OFFSET (0x00e05000)

#define PAGE_SIZE (1 << 12)
#define PAGE_MASK (~(PAGE_SIZE - 1))
#define PAGE_OFFSET(page) (page & (PAGE_SIZE - 1))

    uint32_t dmanum_to_offset(int dmanum);

    /*
     * PWM Function : pwm功能
     */

#define RPI_PWM_CHANNELS 2

    struct pwm_t
    {
        uint32_t ctl;
#define RPI_PWM_CTL_MSEN2 (1 << 15)
#define RPI_PWM_CTL_USEF2 (1 << 13)
#define RPI_PWM_CTL_POLA2 (1 << 12)
#define RPI_PWM_CTL_SBIT2 (1 << 11)
#define RPI_PWM_CTL_RPTL2 (1 << 10)
#define RPI_PWM_CTL_MODE2 (1 << 9)
#define RPI_PWM_CTL_PWEN2 (1 << 8)
#define RPI_PWM_CTL_MSEN1 (1 << 7)
#define RPI_PWM_CTL_CLRF1 (1 << 6)
#define RPI_PWM_CTL_USEF1 (1 << 5)
#define RPI_PWM_CTL_POLA1 (1 << 4)
#define RPI_PWM_CTL_SBIT1 (1 << 3)
#define RPI_PWM_CTL_RPTL1 (1 << 2)
#define RPI_PWM_CTL_MODE1 (1 << 1)
#define RPI_PWM_CTL_PWEN1 (1 << 0)
        uint32_t sta;
#define RPI_PWM_STA_STA4 (1 << 12)
#define RPI_PWM_STA_STA3 (1 << 11)
#define RPI_PWM_STA_STA2 (1 << 10)
#define RPI_PWM_STA_STA1 (1 << 9)
#define RPI_PWM_STA_BERR (1 << 8)
#define RPI_PWM_STA_GAP04 (1 << 7)
#define RPI_PWM_STA_GAP03 (1 << 6)
#define RPI_PWM_STA_GAP02 (1 << 5)
#define RPI_PWM_STA_GAP01 (1 << 4)
#define RPI_PWM_STA_RERR1 (1 << 3)
#define RPI_PWM_STA_WERR1 (1 << 2)
#define RPI_PWM_STA_EMPT1 (1 << 1)
#define RPI_PWM_STA_FULL1 (1 << 0)
        uint32_t dmac;
#define RPI_PWM_DMAC_ENAB (1 << 31)
#define RPI_PWM_DMAC_PANIC(val) ((val & 0xff) << 8)
#define RPI_PWM_DMAC_DREQ(val) ((val & 0xff) << 0)
        uint32_t resvd_0x0c;
        uint32_t rng1;
        uint32_t dat1;
        uint32_t fif1;
        uint32_t resvd_0x1c;
        uint32_t rng2;
        uint32_t dat2;
    } __attribute__((packed, aligned(4)));

#define PWM_OFFSET (0x0020c000)
#define PWM_PERIPH_PHYS (0x7e20c000)

    struct pwm_pin_table_t
    {
        int pinnum;
        int altnum;
        pwm_pin_table_t(int _pinnum, int _altnum) : pinnum(_pinnum), altnum(_altnum){};
    };

    struct pwm_pin_tables_t
    {
        const pwm_pin_table_t *pins;
        size_t count;
    };

    int pwm_pin_alt(int chan, int pinnum);

    //
    // WS2812b function ws2812b核心功能
    //

#define WS2811_TARGET_FREQ 800000 // Can go as low as 400000

// 4 color R, G, B and W ordering
#define SK6812_STRIP_RGBW 0x18100800
#define SK6812_STRIP_RBGW 0x18100008
#define SK6812_STRIP_GRBW 0x18081000
#define SK6812_STRIP_GBRW 0x18080010
#define SK6812_STRIP_BRGW 0x18001008
#define SK6812_STRIP_BGRW 0x18000810
#define SK6812_SHIFT_WMASK 0xf0000000

// 3 color R, G and B ordering
#define WS2811_STRIP_RGB 0x00100800
#define WS2811_STRIP_RBG 0x00100008
#define WS2811_STRIP_GRB 0x00081000
#define WS2811_STRIP_GBR 0x00080010
#define WS2811_STRIP_BRG 0x00001008
#define WS2811_STRIP_BGR 0x00000810

// predefined fixed LED types
#define WS2812_STRIP WS2811_STRIP_GRB
#define SK6812_STRIP WS2811_STRIP_GRB
#define SK6812W_STRIP SK6812_STRIP_GRBW

    struct ws2811_device;

    typedef uint32_t ws2811_led_t; //< 0xWWRRGGBB
    struct ws2811_channel_t
    {
        int gpionum;        //< GPIO Pin with PWM alternate function, 0 if unused
        int invert;         //< Invert output signal
        int count;          //< Number of LEDs, 0 if channel is unused
        int strip_type;     //< Strip color layout -- one of WS2811_STRIP_xxx constants
        ws2811_led_t *leds; //< LED buffers, allocated by driver based on count
        uint8_t brightness; //< Brightness value between 0 and 255
        uint8_t wshift;     //< White shift value
        uint8_t rshift;     //< Red shift value
        uint8_t gshift;     //< Green shift value
        uint8_t bshift;     //< Blue shift value
    };

    struct ws2811_t
    {
        uint64_t render_wait_time;    //< time in µs before the next render can run
        struct ws2811_device *device; //< Private data for driver use
        const rpi_hw_t *rpi_hw;       //< RPI Hardware Information
        uint32_t freq;                //< Required output frequency
        int dmanum;                   //< DMA number _not_ already in use
        ws2811_channel_t channel[RPI_PWM_CHANNELS];
    };

#define WS2811_RETURN_STATES(X)                                                      \
    X(0, WS2811_SUCCESS, "Success"),                                                 \
        X(-1, WS2811_ERROR_GENERIC, "Generic failure"),                              \
        X(-2, WS2811_ERROR_OUT_OF_MEMORY, "Out of memory"),                          \
        X(-3, WS2811_ERROR_HW_NOT_SUPPORTED, "Hardware revision is not supported"),  \
        X(-4, WS2811_ERROR_MEM_LOCK, "Memory lock failed"),                          \
        X(-5, WS2811_ERROR_MMAP, "mmap() failed"),                                   \
        X(-6, WS2811_ERROR_MAP_REGISTERS, "Unable to map registers into userspace"), \
        X(-7, WS2811_ERROR_GPIO_INIT, "Unable to initialize GPIO"),                  \
        X(-8, WS2811_ERROR_PWM_SETUP, "Unable to initialize PWM"),                   \
        X(-9, WS2811_ERROR_MAILBOX_DEVICE, "Failed to create mailbox device"),       \
        X(-10, WS2811_ERROR_DMA, "DMA error"),                                       \
        X(-11, WS2811_ERROR_ILLEGAL_GPIO, "Selected GPIO not possible")

#define WS2811_RETURN_STATES_ENUM(state, name, str) name = state
#define WS2811_RETURN_STATES_STRING(state, name, str) str

    typedef enum
    {
        WS2811_RETURN_STATES(WS2811_RETURN_STATES_ENUM),

        WS2811_RETURN_STATE_COUNT
    } ws2811_return_t;

    class ws2811
    {
    private:
        /* data */
    public:
        ws2811_return_t ws2811_init(ws2811_t *ws2811);                    //< Initialize buffers/hardware
        void ws2811_fini(ws2811_t *ws2811);                               //< Tear it all down
        ws2811_return_t ws2811_render(ws2811_t *ws2811);                  //< Send LEDs off to hardware
        ws2811_return_t ws2811_wait(ws2811_t *ws2811);                    //< Wait for DMA completion
        const char *ws2811_get_return_t_str(const ws2811_return_t state); //< Get string representation of the given return state
    };
}